#include "../include/logger.h"
#include "spdlog/logger.h"
#include "spdlog/sinks/rotating_file_sink.h"
#include "spdlog/async.h"
#include <cstdarg>
#include <memory>
#include <string.h>

namespace
{

#define ROOT_LOGGER "root_logger"

std::shared_ptr<spdlog::logger> g_logger;

class LogConfig
{
public:
    int threadPoolCount{1};//默认一个线程
    int threadQueueSize{24576};//默认
    std::string logRootPath{"./"};//日志根路径
    std::string logFile{"rosout.log"};//日志文件
    int maxFileSize{10};//文件回滚size，单位：M
    int maxFileCount{2};//最多备份文件数量，1表示最多备份1个文件，即一共2个日志文件
    int flushTimeInterval{2};//刷新时间间隔，单位：s
    std::string logPattern{"%Y-%m-%d %H:%M:%S.%e [%l] [%t]  %v"};//文件格式，默认：日期 日志级别 [线程号] 数据
    std::string flushLevel{"warn"};//刷新等级，debug/info/warn/error/fault
    std::string level{"info"};//日志记录等级，debug/info/warn/error/fault
};

int getIntEnv(const char *name, int default_value)
{
    char *env = getenv(name);
    if (env)
    {
        return std::stoi(env);
    }
    return default_value;
}

std::string getStringEnv(const char *name, const std::string &default_value)
{
    char *env = getenv(name);
    if (env)
    {
        return env;
    }
    return default_value;
}

bool getBoolEnv(const char *name, bool default_value)
{
    char *env = getenv(name);
    if (env)
    {
        if (strncmp(env, "true", strlen(env)) == 0)
        {
            return true;
        }
        return false;
    }
    return default_value;
}

std::string formatStr(const char *fmt, va_list args)
{
    va_list arg_copy;
    va_copy(arg_copy, args);

    auto size_buf = vsnprintf(nullptr, 0, fmt, args) + 1;
    std::unique_ptr<char[]> buf(new(std::nothrow) char[size_buf]);

    if (!buf)
        return "";

    vsnprintf(buf.get(), size_buf, fmt, arg_copy);
    va_end(arg_copy);
    return std::string(buf.get(), buf.get() + size_buf - 1);
}

LogConfig getLogConfig()
{
    LogConfig logConfig;
    //threadPoolCount
    logConfig.threadPoolCount = getIntEnv("spdlog_thread_pool_count", logConfig.threadPoolCount);
    logConfig.threadQueueSize = getIntEnv("spdlog_thread_queue_size", logConfig.threadQueueSize);
    logConfig.logRootPath = getStringEnv("spdlog_log_root_path", logConfig.logRootPath);
    logConfig.logFile = getStringEnv("spdlog_log_file", logConfig.logFile);
    logConfig.maxFileSize = getIntEnv("spdlog_max_file_size", logConfig.maxFileSize);
    logConfig.maxFileCount = getIntEnv("spdlog_max_file_count", logConfig.maxFileCount);
    logConfig.logPattern = getStringEnv("spdlog_log_pattern", logConfig.logPattern);
    logConfig.flushLevel = getStringEnv("spdlog_flush_level", logConfig.flushLevel);
    logConfig.level = getStringEnv("spdlog_level", logConfig.level);
    logConfig.flushTimeInterval = getIntEnv("spdlog_flush_time_interval", logConfig.flushTimeInterval);
    return logConfig;
}

}

namespace logger
{
void initLogger(const std::string& logDir)
{
    LogConfig logConfig = getLogConfig();
    std::string log = logConfig.logRootPath + "/" + logDir + "/" + logConfig.logFile;
    g_logger = spdlog::create_async<spdlog::sinks::rotating_file_sink_mt>(
        ROOT_LOGGER, log, 1024 * 1024 * logConfig.maxFileSize, logConfig.maxFileCount);

    g_logger->set_pattern(logConfig.logPattern);
    if (logConfig.level == "debug")
    {
        g_logger->set_level(spdlog::level::debug);
    }
    else if (logConfig.level == "warn")
    {
        g_logger->set_level(spdlog::level::warn);
    }
    else if (logConfig.level == "error")
    {
        g_logger->set_level(spdlog::level::err);
    }
    else if (logConfig.level == "fault")
    {
        g_logger->set_level(spdlog::level::critical);
    }
    else
    {
        g_logger->set_level(spdlog::level::info);
    }
    if (logConfig.flushLevel == "debug")
    {
        g_logger->flush_on(spdlog::level::debug);
    }
    else if (logConfig.flushLevel == "info")
    {
        g_logger->flush_on(spdlog::level::info);
    }
    else if (logConfig.flushLevel == "error")
    {
        g_logger->flush_on(spdlog::level::err);
    }
    else if (logConfig.flushLevel == "fault")
    {
        g_logger->flush_on(spdlog::level::critical);
    }
    else
    {
        g_logger->flush_on(spdlog::level::warn);
    }
}
void debugLog(const MessageLogContext &context, const char *fmt, ...)
{
    va_list arg_list;
    va_start(arg_list, fmt);
    auto str = formatStr(fmt, arg_list);
    va_end(arg_list);

    printf("%s\n", str.c_str());
    if (g_logger)
    {
        g_logger->debug(str);
    }
}
void infoLog(const MessageLogContext &context, const char *fmt, ...)
{
    va_list arg_list;
    va_start(arg_list, fmt);
    auto str = formatStr(fmt, arg_list);
    va_end(arg_list);

    printf("%s\n", str.c_str());
    if (g_logger)
    {
        g_logger->info(str);
    }
}
void warningLog(const MessageLogContext &context, const char *fmt, ...)
{
    va_list arg_list;
    va_start(arg_list, fmt);
    auto str = formatStr(fmt, arg_list);

    printf("%s\n", str.c_str());
    if (g_logger)
    {
        g_logger->warn(str);
    }
}
void errorLog(const MessageLogContext &context, const char *fmt, ...)
{
    va_list arg_list;
    va_start(arg_list, fmt);
    auto str = formatStr(fmt, arg_list);
    va_end(arg_list);

    fprintf(stderr, "%s\n", str.c_str());
    if (g_logger)
    {
        g_logger->error(str);
    }
}
void fatalLog(const MessageLogContext &context, const char *fmt, ...)
{
    va_list arg_list;
    va_start(arg_list, fmt);
    auto str = formatStr(fmt, arg_list);
    va_end(arg_list);

    fprintf(stderr, "%s\n", str.c_str());
    if (g_logger)
    {
        g_logger->error(str);
    }
}

}


